const fs = require('fs');
const path = require('path');
const axios = require('axios');
const { ensureDirectory, formatFileSize, calculateSpeed, cleanupTempFiles } = require('./utils');

/**
 * 单个下载任务类
 * @class DownloadTask
 */
class DownloadTask {
    /**
     * 构造函数
     * @param {string} url - 下载URL
     * @param {string} outputPath - 输出路径
     * @param {Object} options - 配置选项
     * @param {number} [options.chunkSize=1024*1024] - 分片大小(字节)
     * @param {number} [options.maxRetries=3] - 最大重试次数
     * @param {number} [options.timeout=30000] - 请求超时时间(毫秒)
     * @param {Function} [options.onProgress] - 进度回调
     * @param {Function} [options.onFinish] - 完成回调
     * @param {Function} [options.onError] - 错误回调
     * @param {number} [options.speedLimit=0] - 速度限制(字节/秒)
     */
    constructor(url, outputPath, options = {}) {
        this.url = url;
        this.outputPath = outputPath;
        this.options = {
            chunkSize: options.chunkSize || 1024 * 1024, // 默认1MB
            maxRetries: options.maxRetries || 3,
            timeout: options.timeout || 30000,
            speedLimit: options.speedLimit || 0,
            ...options
        };

        // 任务状态
        this.status = 'pending'; // pending, downloading, paused, completed, failed
        this.downloadedBytes = 0;
        this.totalBytes = 0;
        this.startTime = null;
        this.speed = 0;
        this.progress = 0;

        // 分片相关
        this.chunks = [];
        this.activeConnections = 0;
        this.maxConnections = 5; // 最大并发连接数

        // 临时文件相关
        this.tempDir = path.join(path.dirname(this.outputPath), '.temp');
        this.tempFile = path.join(this.tempDir, path.basename(this.outputPath) + '.tmp');

        // 初始化
        ensureDirectory(path.dirname(this.outputPath));
        ensureDirectory(this.tempDir);
    }

    /**
     * 开始下载任务
     * @async
     */
    async start() {
        if (this.status === 'downloading') return;

        try {
            this.status = 'downloading';
            this.startTime = Date.now();

            // 检查是否支持分片下载
            const acceptRanges = await this.checkAcceptRanges();

            if (acceptRanges && this.options.chunkSize > 0) {
                await this.downloadWithChunks();
            } else {
                await this.downloadAsWhole();
            }

            this.status = 'completed';
            this.options.onFinish?.({
                path: this.outputPath,
                size: this.totalBytes,
                time: (Date.now() - this.startTime) / 1000
            });

            // 清理临时文件
            cleanupTempFiles(this.tempFile);
        } catch (error) {
            this.status = 'failed';
            this.options.onError?.(error);
        }
    }

    /**
     * 暂停下载任务
     */
    pause() {
        if (this.status === 'downloading') {
            this.status = 'paused';
            // 取消所有正在进行的请求
            this.chunks.forEach(chunk => {
                if (chunk.controller) {
                    chunk.controller.abort();
                }
            });
        }
    }

    /**
     * 恢复下载任务
     */
    resume() {
        if (this.status === 'paused') {
            this.start();
        }
    }

    /**
     * 停止下载任务
     */
    stop() {
        this.status = 'stopped';
        // 取消所有正在进行的请求
        this.chunks.forEach(chunk => {
            if (chunk.controller) {
                chunk.controller.abort();
            }
        });
        // 清理临时文件
        cleanupTempFiles(this.tempFile);
    }

    /**
     * 检查服务器是否支持分片下载
     * @async
     * @return {Promise<boolean>} 是否支持分片
     */
    async checkAcceptRanges() {
        try {
            const response = await axios.head(this.url, {
                timeout: this.options.timeout
            });

            this.totalBytes = parseInt(response.headers['content-length']) || 0;
            return response.headers['accept-ranges'] === 'bytes';
        } catch (error) {
            console.warn('Failed to check accept-ranges, fallback to whole download:', error.message);
            return false;
        }
    }

    /**
     * 整文件下载
     * @async
     */
    async downloadAsWhole() {
        const response = await axios({
            method: 'get',
            url: this.url,
            responseType: 'stream',
            timeout: this.options.timeout
        });

        this.totalBytes = parseInt(response.headers['content-length']) || 0;

        const writer = fs.createWriteStream(this.tempFile);
        response.data.pipe(writer);

        // 限速处理
        if (this.options.speedLimit > 0) {
            this.throttleStream(response.data, this.options.speedLimit);
        }

        // 进度更新
        let lastUpdate = 0;
        response.data.on('data', (chunk) => {
            this.downloadedBytes += chunk.length;

            // 限制进度更新频率(每秒最多10次)
            const now = Date.now();
            if (now - lastUpdate > 100) {
                this.updateProgress();
                lastUpdate = now;
            }
        });

        await new Promise((resolve, reject) => {
            writer.on('finish', () => {
                fs.renameSync(this.tempFile, this.outputPath);
                resolve();
            });
            writer.on('error', reject);
        });
    }

    /**
     * 分片下载
     * @async
     */
    async downloadWithChunks() {
        // 初始化分片
        this.initChunks();

        // 下载所有分片
        await Promise.all(
            this.chunks
            .filter(chunk => !chunk.completed)
            .slice(0, this.maxConnections) // 限制并发数
            .map(chunk => this.downloadChunk(chunk))
        );

        // 合并分片
        await this.mergeChunks();
    }

    /**
     * 初始化分片信息
     */
    initChunks() {
        this.chunks = [];
        const chunkCount = Math.ceil(this.totalBytes / this.options.chunkSize);

        for (let i = 0; i < chunkCount; i++) {
            const start = i * this.options.chunkSize;
            const end = Math.min(start + this.options.chunkSize - 1, this.totalBytes - 1);

            this.chunks.push({
                index: i,
                start,
                end,
                completed: false,
                downloaded: 0,
                controller: null
            });
        }
    }

    /**
     * 下载单个分片
     * @async
     * @param {Object} chunk - 分片对象
     */
    async downloadChunk(chunk) {
        if (this.status !== 'downloading') return;

        this.activeConnections++;
        const chunkPath = this.getChunkPath(chunk.index);
        const writer = fs.createWriteStream(chunkPath);

        let retries = 0;
        let lastPosition = chunk.start;

        while (retries < this.options.maxRetries) {
            try {
                const controller = new AbortController();
                chunk.controller = controller;

                const response = await axios({
                    method: 'get',
                    url: this.url,
                    responseType: 'stream',
                    headers: {
                        Range: `bytes=${lastPosition}-${chunk.end}`
                    },
                    signal: controller.signal,
                    timeout: this.options.timeout
                });

                // 限速处理
                if (this.options.speedLimit > 0) {
                    this.throttleStream(response.data, this.options.speedLimit / this.maxConnections);
                }

                // 写入文件
                response.data.pipe(writer);

                // 进度更新
                response.data.on('data', (data) => {
                    chunk.downloaded += data.length;
                    this.downloadedBytes += data.length;
                    this.updateProgress();
                });

                await new Promise((resolve, reject) => {
                    writer.on('finish', resolve);
                    writer.on('error', reject);
                });

                chunk.completed = true;
                break;
            } catch (error) {
                retries++;
                if (retries >= this.options.maxRetries) {
                    throw error;
                }
                await new Promise(resolve => setTimeout(resolve, 1000 * retries));
            } finally {
                chunk.controller = null;
            }
        }

        this.activeConnections--;

        // 如果有其他分片未完成且当前连接数不足，继续下载其他分片
        if (this.activeConnections < this.maxConnections) {
            const nextChunk = this.chunks.find(c => !c.completed && !c.controller);
            if (nextChunk) {
                await this.downloadChunk(nextChunk);
            }
        }
    }

    /**
     * 合并所有分片
     * @async
     */
    async mergeChunks() {
        const writer = fs.createWriteStream(this.tempFile);

        for (let i = 0; i < this.chunks.length; i++) {
            const chunkPath = this.getChunkPath(i);
            const reader = fs.createReadStream(chunkPath);

            await new Promise((resolve) => {
                reader.pipe(writer, { end: false });
                reader.on('end', () => {
                    // 删除临时分片文件
                    fs.unlinkSync(chunkPath);
                    resolve();
                });
            });
        }

        writer.end();
        await new Promise((resolve) => {
            writer.on('finish', () => {
                fs.renameSync(this.tempFile, this.outputPath);
                resolve();
            });
        });
    }

    /**
     * 获取分片文件路径
     * @param {number} index - 分片索引
     * @return {string} 分片文件路径
     */
    getChunkPath(index) {
        return path.join(this.tempDir, `chunk_${index}.tmp`);
    }

    /**
     * 更新下载进度
     */
    updateProgress() {
        if (this.totalBytes > 0) {
            this.progress = (this.downloadedBytes / this.totalBytes) * 100;
            this.speed = calculateSpeed(this.downloadedBytes, (Date.now() - this.startTime) / 1000);

            this.options.onProgress?.({
                progress: this.progress,
                downloaded: this.downloadedBytes,
                total: this.totalBytes,
                speed: this.speed,
                time: (Date.now() - this.startTime) / 1000
            });
        }
    }

    /**
     * 限速流
     * @param {Stream} stream - 可读流
     * @param {number} speedLimit - 速度限制(字节/秒)
     */
    throttleStream(stream, speedLimit) {
        let chunkCount = 0;
        let startTime = Date.now();

        const originalPipe = stream.pipe;
        stream.pipe = function(destination, options) {
            const newStream = originalPipe.call(this, destination, options);

            newStream.on('data', (chunk) => {
                chunkCount += chunk.length;

                const elapsed = (Date.now() - startTime) / 1000;
                const expectedTime = chunkCount / speedLimit;

                if (elapsed < expectedTime) {
                    const delay = (expectedTime - elapsed) * 1000;
                    newStream.pause();
                    setTimeout(() => newStream.resume(), delay);
                }
            });

            return newStream;
        };
    }

    /**
     * 获取任务状态快照
     * @return {Object} 任务状态
     */
    getStateSnapshot() {
        return {
            url: this.url,
            outputPath: this.outputPath,
            options: this.options,
            status: this.status,
            downloadedBytes: this.downloadedBytes,
            totalBytes: this.totalBytes,
            chunks: this.chunks.map(chunk => ({
                index: chunk.index,
                start: chunk.start,
                end: chunk.end,
                completed: chunk.completed,
                downloaded: chunk.downloaded
            })),
            tempDir: this.tempDir,
            tempFile: this.tempFile,
            timestamp: getCurrentTimestamp()
        };
    }

    /**
     * 从状态快照恢复任务
     * @param {Object} state - 任务状态
     */
    restoreFromState(state) {
        this.url = state.url;
        this.outputPath = state.outputPath;
        this.options = state.options;
        this.status = state.status;
        this.downloadedBytes = state.downloadedBytes;
        this.totalBytes = state.totalBytes;
        this.chunks = state.chunks || [];
        this.tempDir = state.tempDir;
        this.tempFile = state.tempFile;

        // 恢复分片下载状态
        if (this.status === 'downloading' || this.status === 'paused') {
            this.status = 'paused'; // 恢复时默认暂停状态
        }
    }
}

module.exports = DownloadTask;